import { h, render, isVNode, nextTick } from 'vue';
import Message from './main.vue';
import Content from './content.vue';

const positions = ['tl', 'tc', 'tr', 'bl', 'bc', 'br'];
const defaultPosition = 'tc';
const blocks = {};
const defaultConfig = { offset: 0, space: 16, alive: Infinity };

positions.forEach(p => blocks[p] = { ...defaultConfig, list: [] });

function nextOffset(block) {
	return block.list.reduce((r, n) => {
		return r + n.el.offsetHeight + block.space;
	}, block.offset + block.space);
}

function closeMessage(vm) {
	const block = blocks[vm.component.props.position];
	const index = block.list.findIndex(instance => instance === vm);
	if (index < 0) return;
	const removeOffset = vm.el.offsetHeight + block.space;
	block.list.splice(index, 1);
	for (let i = index; i < block.list.length; i++) {
		block.list[i].component.props.offset -= removeOffset;
	}
}

function removeMessage(vm) {
	vm.el.remove();
	vm = null;
}

function separateProps(props) {
	return Object.entries(props).reduce((r, n) => {
		const [key, value] = n;
		if (Message.props.hasOwnProperty(key)) {
			r[0][key] = value;
		} else {
			r[1][key] = value;
		}
		return r;
	}, [{}, {}]); 
}

function pluginMessage(message, options = {}) {
	const { ContentConstructor = Content, target = 'body', position = defaultPosition, ...opts } = options; 
	const block = blocks[position];
	if (!block) return;
	if (block.list.length >= block.alive) {
		for (let i = 0; i < block.list.length - block.alive + 1; i++) {
			block.list[i].component.exposed.close();
		}
	}
	const [MessageProps, ContentProps] = separateProps(opts);
	const vm = h(Message, {
		...MessageProps,
		position,
		offset: nextOffset(block),
		onClose: () => closeMessage(vm),
		onRemove: () => removeMessage(vm)
	}, { 
		default: () => h(ContentConstructor, {
			...ContentProps,
			onClose: () => vm.component.exposed.close()
		}, {
			default: () =>  isVNode(message) ? message : h('span', null, message)
		})
	});
	render(vm, document.createDocumentFragment());
	document.querySelector(target).appendChild(vm.el);
	block.list.push(vm);
	return vm.component.exposed.close;
}

pluginMessage.setConfig = function(position, config) {
	!!blocks[position] && Object.assign(blocks[position], config);
}

pluginMessage.resetConfig = function(position) {
	!!blocks[position] && Object.assign(blocks[position], defaultConfig);
}

pluginMessage.closeAll = function() {
	Object.values(blocks).forEach(block => {
		block.list.forEach(vm => vm.component.exposed.close());
	})
}

pluginMessage.multiple = function(messages) {
	if (messages?.[0]) {
		const { message, ...options } = messages.shift();
		pluginMessage(message, options);
		nextTick().then(() => {
			pluginMessage.multiple(messages);
		})
	}
}

pluginMessage.install = function(app) {
	app.config.globalProperties.$LuoMessage = pluginMessage;
};

const types = ['success', 'info', 'warning', 'error', 'notice'];
types.forEach(type => {
	pluginMessage[type] = function(message, options) {
		return pluginMessage(message, { type, ...options });
	}
})

export default pluginMessage;